//
// CUDA Sagan Simulator - a CUDA version fo the sagan Simulator Software
// version 1.0 - Alpha 01
// by TTYMan
//
// para compilar:  nvcc engine.cu -o teste1 -I/home/ttyman/NVIDIA_GPU_Computing_SDK/C/common/inc

#include <stdio.h>
#include <string.h>
#include <cutil.h>
//cutil.h ==> /home/ttyman/NVIDIA_GPU_Computing_SDK/C/common/inc

#define BSIZE 256
#define BUFFER_MAX 2048      // buffer size for reading each line of initial conditions file.
#define offset      8        // each offset space is used to save one particle information:
                             //   0 - Pos X ==> array[index*offset+0]
                             //   1 - Pos Y ==> array[index*offset+1]
                             //   2 - Pos Z
                             //   3 - Mass
                             //   4 - Vel X
                             //   5 - Vel Y
                             //   6 - Vel Z
                             //   7 - not used
                             // this approuch is useful to take advantage of CUDA float3 and float4 datatypes.

// declaracoes memoria device

// aloca array na memoria global da GPU
__device__ float4*  d_ParticleArray;


// declara o kernel de teste
__global__ void AtualizaVetor(float4* d_ParticleArray, int* d_Contador)
{
    __shared__ int Acumulador ;

    int indice = threadIdx.x;
    d_ParticleArray[indice].x /= 2.0f;

    __syncthreads();

    Acumulador += d_ParticleArray[indice].w;

    __syncthreads();

    *d_Contador = Acumulador;
}


float* LoadInitialConditionsData(char* FileName, int* N)
{
    FILE* fptr;

    if ((fptr = fopen(FileName, "r")))
    {
        char    LineBuffer[BUFFER_MAX];
        int     LineCount = 0;

        // The first line of the initial conditions file should contain the amount of particles information.
        // So, read this value and use it for allocate memory for the particles array.
        fgets (LineBuffer, BUFFER_MAX, fptr);
        sscanf(LineBuffer, "%d ", &LineCount);
        *N = LineCount; // 'N' value finally discovered!

        // allocate memory for the Particles array.
        float* ParticleArray = (float*)malloc( sizeof(float) * offset * LineCount );

        // read the file line by line, saving position and velocity data into arrays.
        // each file line represents one particle.
        for (int i=0; i<LineCount; i++)
        {
            fgets (LineBuffer, BUFFER_MAX, fptr);
            sscanf(LineBuffer, "%f %f %f %f %f %f %f " , &ParticleArray[i*offset+0],      /* PosX     */
                                                         &ParticleArray[i*offset+1],      /* PosY     */
                                                         &ParticleArray[i*offset+2],      /* PosZ     */
                                                         &ParticleArray[i*offset+3],      /* Mass     */
                                                         &ParticleArray[i*offset+4],      /* VelX     */
                                                         &ParticleArray[i*offset+5],      /* VelY     */
                                                         &ParticleArray[i*offset+6]);     /* VelZ     */
                                                          ParticleArray[i*offset+7]=0.0f; /* Not Used */

        }

        //close the file and release ay used resource
        fclose(fptr);

        return ParticleArray;

    }
    else
    {
        //Error! Couldn't open the file!
        printf("\nError opening file %s!\n", FileName);
        return 0;
    }
}




int main(int argc, char **argv)
{
    printf("\nOla, mundo!!!\n");

    char    Arquivo[] = "./data/esfera395CUDA.ini";
    int     N = 0;

    float* ParticleArray;
    int *Contador;
    int *d_Contador;

    ParticleArray = LoadInitialConditionsData(Arquivo, &N);

    size_t tamanho = N * sizeof(float4);

    Contador = (int*)malloc(sizeof(int));

    // aloca memoria no device para o array de particulas
    CUDA_SAFE_CALL (    cudaMalloc((void**)&d_ParticleArray, tamanho )     );

    CUDA_SAFE_CALL (    cudaMalloc((void**)&d_Contador, sizeof(int)  )     );

    // copia o array de particulas para a memoria do device
    CUDA_SAFE_CALL( cudaMemcpy( d_ParticleArray, ParticleArray, tamanho, cudaMemcpyHostToDevice )    );

    //CUDA_SAFE_CALL( cudaMemcpy( d_Contador, Contador, sizeof(int), cudaMemcpyHostToDevice )    );

    AtualizaVetor<<<1,N>>>(d_ParticleArray, d_Contador);


    // aloca memoria no host para receber o array ja processado pelo kernel
    //float4* ParticleArrayRetorno = (float4*)malloc( sizeof(float4) * N );
    float* ParticleArrayRetorno = (float*)malloc( sizeof(float) * offset * N );

    // copia o array da memoria da GPU devolta para a memoria do host
    CUDA_SAFE_CALL( cudaMemcpy( ParticleArrayRetorno, d_ParticleArray, tamanho, cudaMemcpyDeviceToHost )    );

    CUDA_SAFE_CALL( cudaMemcpy( Contador, d_Contador, sizeof(int), cudaMemcpyDeviceToHost )    );

    // compara o valor dos dois arrays
    printf("\n Comparacao valores\n=====================\n");
    for(int i =0; i<N; i++)
    {
	printf("Contador: %d :: [%d]: Xo: %f - Xn: %f - Yo: %f - Yn: %f \n", *Contador, i, ParticleArray[i*offset], ParticleArrayRetorno[i*offset], ParticleArray[i*offset+1], ParticleArrayRetorno[i*offset+1]);
    }








	// teste teste
    int devID;
    cudaDeviceProp props;
    CUDA_SAFE_CALL( cudaGetDevice(&devID) );
    CUDA_SAFE_CALL( cudaGetDeviceProperties(&props, devID) );



    printf("\nValor de N: %d\n", N);

    // // teste teste
    // for(int i=0; i<N; i++)
            // printf("main i=%d: %f %f %f %f %f %f %f \n" , i,          ParticleArray[i*offset+0],      /* PosX     */
                                                         // ParticleArray[i*offset+1],      /* PosY     */
                                                         // ParticleArray[i*offset+2],      /* PosZ     */
                                                         // ParticleArray[i*offset+3],      /* Mass     */
                                                         // ParticleArray[i*offset+4],      /* VelX     */
                                                         // ParticleArray[i*offset+5],      /* VelY     */
                                                         // ParticleArray[i*offset+6]);     /* VelZ     */

    printf("\n\nYou ran me by typing: %s\n", argv[0]);
    printf("\nPlaca: %s\n", props.name);
    return (0);
}
